﻿/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
import com.bourre.events.BasicEvent;
import com.bourre.events.EventBroadcaster;
import com.bourre.events.EventType;
import com.bourre.events.IEvent;
import com.bourre.events.IEventDispatcher;
import com.bourre.log.PixlibStringifier;
import com.bourre.transitions.IFrameListener;
import com.kairos.engine.RTEvent;
import com.kairos.engine.RTObject;


/**
 * A {@code Clock} object is usefull to measure time 
 * in an game build with a {@code RTBeacon}.
 * 
 * <p>A {@code Clock} object can also be used to create
 * time limited game. Just pass a value in the constructor
 * to set the end time of the clock. The time is set in milliseconds.</p>
 * 
 * <p>{@code Clock} time scale is affected by the {@code RTBeacon} global
 * speed and max step size.</p>
 *	
 * @author	Cédric Néhémie
 * @see		RTObject	
 * @see		IFrameListener	
 * @example import com.kairos.utils.Clock;
 * 
 * var myClock : Clock = new Clock ();
 * 
 * myClock.start();
 * 
 * function onEnterFrame ()
 * {
 * 	trace ( myClock.elapsed() );
 * }
 * @example import com.kairos.utils.Clock;
 * 
 * var myClock : Clock = new Clock ( 10000 );
 * 
 * myClock.addEventListener ( Clock.onClockEndEvent, this );
 * 
 * myClock.start ();
 * 
 * function onClockEnd ()
 * {
 * 	trace ( "Clock ended after : " + myClock.elapsed() + " ms" );
 * }
 * @example // Using a clock to display the running time in a game.
 * import com.kairos.utils.Clock;
 * 
 * var myClock : Clock = new Clock ();
 * 
 * myClock.start();
 * 
 * function onEnterFrame ()
 * {
 * 	var d : Date = myClock.toDate();
 * 	trace ( d.getMinutes() + ":" + d.getSeconds() + "." + d.getMilliseconds() );
 * }
 */
class com.kairos.utils.Clock extends RTObject implements IFrameListener
{
	// Elapsed time
	private var _nE : Number;
	// Max time
	private var _nLimit : Number;
	// Event broadcast composition
	private var _oEB:EventBroadcaster;
	// Event to broadcast
	private var _eOnClockEndEvent : BasicEvent;
	
	public static var onClockEndEVENT : EventType = new EventType ( "onClockEnd" );

	/**
	 * Create a new {@code Click} object.
	 * 
	 * <p>The optional argument let you set a time limit in milliseconds 
	 * before the clock dispatch the {@code onClockEndEVENT} event. 
	 * If you not set the limit or if you set it to 0, the clock never 
	 * dispatch any event.</p>
	 * 
	 * @param	limit	The max time before the clock dispatch the
	 * {@code onClockEndEVENT} event.
	 */
	public function Clock ( limit : Number )
	{
		_oEB = new EventBroadcaster( this );
		_eOnClockEndEvent = new BasicEvent( onClockEndEVENT, this);
		
		_nE = 0;
		setLimit ( limit );
	} 
	
	/**
	 * Realize the time measurement.
	 * 
	 * <p>If a limit have been defined, and if elapsed time is greater than
	 * the limit, the end event is dispatched.</p>
	 */
	public function onEnterFrame () : Void
	{
		_nE += getLocalStep( arguments[ 0 ] );
		
		if( _nLimit == 0 ) return;
		
		if( _nE > _nLimit )
		{
			 _oEB.broadcastEvent ( _eOnClockEndEvent );
			 stop();
		}
	}
	
	/**
	 * Set a time limit before the clock dispatch the {@code onClockEndEVENT}
	 * event. If you not set the limit or if you set it to 0, the clock 
	 * never dispatch any event.
	 *	
	 * @param	limit	The max time before the clock dispatch the
	 * {@code onClockEndEVENT} event.
	 */
	public function setLimit ( limit : Number ) : Void
	{
		_nLimit = limit > 0 ? limit : 0 ;
	}
	
	/**
	 * Return the current time limit of the {@code Clock} object.
	 * 	
	 * @return Number of milliseconds before the clock stop.	
	 */
	public function getLimit () : Number
	{
		return _nLimit;
	}
	
	/**
	 * Returns the total time elapsed in milliseconds since {@code Clock}
	 * start.
	 *	
	 * @return {@code Number}
	 */	
	public function elapsed () : Number
	{
		return _nE;
	}
	
	/**
	 * Adds listener for receiving all events.
	 * 
	 * <p>Example
	 * <code>
	 *   t.addListener( myListener );
	 * </code>
	 * 
	 * @param oL Listener object.
	 */
	public function addListener( oL ) : Void
	{
		_oEB.addListener( oL );
	}
	
	/**
	 * Removes listener for receiving all events.
	 * 
	 * <p>Example
	 * <code>
	 *   t.removeListener( myListener );
	 * </code>
	 * 
	 * @param oL Listener object which implements {@link ITweenListener} interface.
	 */
	public function removeListener( oL ) : Void
	{
		_oEB.removeListener( oL );
	}
	
	/**
	 * Adds listener for specifical event.
	 * 
	 * <p>Example
	 * <code>
	 *   t.addEventListener( Clock.onClockEndEVENT, myListener );
	 * </code>
	 * 
	 * @param t Name of the Event.
	 * @param oL Listener object.
	 */
	public function addEventListener( e : EventType, oL ) : Void
	{
		_oEB.addEventListener.apply( _oEB, arguments );
	}
	
	/**
	 * Removes listener for specifical event.
	 * 
	 * <p>Example
	 * <code>
	 *   t.removeEventListener( Clock.onClockEndEVENT, myListener );
	 * </code>
	 * 
	 * @param t Name of the Event.
	 * @param oL Listener object.
	 */
	public function removeEventListener( e : EventType, oL ) : Void
	{
		_oEB.removeEventListener( e, oL );
	}
	
	
	/**
	 * Returns a {@code Date} object based on the {@code Clock} elapsed time.
	 *	
	 * @return	 A new Date object.
	 */
	public function toDate () : Date
	{
		return new Date ( 0, 0, 0, 0, 0, 0, elapsed() );
	}
	
	/**
	 * Returns a {@code Date} object based on the {@code Clock} remaining time.
	 *	
	 * @return	 A new Date object.
	 */
	public function toDateRemain () : Date
	{
		return new Date ( 0, 0, 0, 0, 0, 0, remain() );
	}
	
	/**
	 * Returns the remaining time until the time limit.
	 * 
	 * <p>If the clock have no time limit the function returns 
	 * the elapsde time instead.</p>
	 * 
	 * @return remaining time until clock end
	 */
	public function remain () : Number
	{
		if( _nLimit != 0 )
		{
			return _nLimit - _nE;
		}
		else
		{
			return _nE;
		}
	}
	
	/**
	 * Returns the String representation of the object.
	 */	
	public function toString() : String
	{
		return PixlibStringifier.stringify( this );
	}
}